package org.midy.infrastructure;

import org.midy.infrastructure.tools.Logger;

import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;

public class ReadEventHandler implements EventHandler{
    // 这里需要进行总线路由： 需要一个详情的基于事件驱动的、线程隔离、同时保持
    @Override
    public Object handle(SelectionKey selectionKey) {
        ChannelEventHelper channelEventHelper = null;
        try {
            channelEventHelper = checkSelectKeyAndChannel(selectionKey);
            if( channelEventHelper == null ) {
                return null;
            }

            SocketChannel socketChannel = channelEventHelper.getSocketChannel();
            ByteBuffer bodyByteBuffer = channelEventHelper.getBodyByteBuffer();

            if(bodyByteBuffer == null) { // 如果bodyByteBuffer == null, 标识历史没有数据, 需要先读Header
                ByteBuffer headByteBuffer = channelEventHelper.getHeadByteBuffer();

                while (headByteBuffer.hasRemaining()) { //用来判断报文头上的长度位是否已经读满
                    int readSize = socketChannel.read(headByteBuffer);
                    if( readSize == -1) {
                        throw new RuntimeException("读数据大小 -1"); // 需要重置连接
                    }
                    if( readSize == 0 ) {  // 没有读到数据，就终止循环读取
                        break;
                    }
                }

                if(headByteBuffer.hasRemaining()) { // 报文头没有读完的情况, 保存读了部分的数据，下次再读
                    channelEventHelper.setHeadByteBuffer(headByteBuffer);
                    return null;
                }

                Integer bodySize = getBodySize(channelEventHelper, headByteBuffer);
                if (bodySize == 0) { //  没有可以读取的报文
                    return null;
                }

                bodyByteBuffer = ByteBuffer.allocate(bodySize);
            }

            while (bodyByteBuffer.hasRemaining() ) {
                int readSize = socketChannel.read(bodyByteBuffer);
                if( readSize == -1 ) {
                    throw new RuntimeException("读数据大小 -1");
                }
                if( readSize == 0 ) {
                    break;
                }
            }

            if(bodyByteBuffer.hasRemaining()) { // 报文头没有读完的情况, 保存读了部分的数据，下次再读(可知还有部分数据未读取到)
                channelEventHelper.recordBodyByteBuffer(bodyByteBuffer);
                selectionKey.attach(channelEventHelper);
                return null;
            }

            return bodyByteBuffer.flip().array();
        } catch (IOException ioe) {
            Logger.warn("连接断开: %s", ioe.getMessage());
            throw new RuntimeException(ioe);
        } catch (IllegalArgumentException iae ){
            Logger.warn("报文长度头解析失败: %s", iae.getMessage());
            throw new RuntimeException(iae);
        } catch (Throwable t) {
            Logger.warn("连接断开: ", t.getMessage());
            throw new RuntimeException(t);
        }
    }

    /**
     * 获取 单个完整报文体大小
     */
    private Integer getBodySize(ChannelEventHelper channelEventHelper, ByteBuffer headByteBuffer) {
        try {
            headByteBuffer.flip();
            byte[] bytes = new byte[channelEventHelper.getPreLength()];
            headByteBuffer.get(bytes);
            Integer headSize = Integer.valueOf(new String(bytes));
            if(headSize < 0) { // 对于（非法或错误）请求, 我们要断开请求（因为目前不支持， 在读乱序后，自动矫正的功能）
                throw new RuntimeException("请求头不能小于0");
            }
            return headSize;
        } catch (Throwable t) {
            throw new IllegalArgumentException("非法请求报文头", t);
        }
    }
}
